﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

using BitcoinExchange;
using BitcoinMarket;
using BitcoinMarket.Market;

namespace BitcoinTrading.Data
{
    public class DataController
    {
        DataMarket _dsMarketData;
        DataArbitrage _dsTradingData;

        MarketPrice _market;
        Exchange _exchange;

        public DataController(MarketPrice market, Exchange exchange)
        {
            _dsMarketData = new DataMarket();
            _dsTradingData = new DataArbitrage();
            _market = market;
            _exchange = exchange;
        }

        public void SetMarketData()
        {
            DataTable dt;

            dt = _market.KorbitMarketAskTable();
            _dsMarketData.SetDataTable(DataMarket.TABLE_KORBIT_ASK, dt);

            dt = _market.KorbitMarketBidTable();
            _dsMarketData.SetDataTable(DataMarket.TABLE_KORBIT_BID, dt);

            dt = _exchange.KorbitOpenOrderTable();
            _dsMarketData.SetDataTable(DataMarket.TABLE_KORBIT_OPEN, dt);

            dt = _market.BTCeMarketAskTable();
            _dsMarketData.SetDataTable(DataMarket.TABLE_BTCE_ASK, dt);

            dt = _market.BTCeMarketBidTable();
            _dsMarketData.SetDataTable(DataMarket.TABLE_BTCE_BID, dt);

            dt = _exchange.BTCeOpenOrderTable();
            _dsMarketData.SetDataTable(DataMarket.TABLE_KORBIT_OPEN, dt);            
        }

        public void CalculateAccmulate()
        {
            CalculateAccTables();

            CalculateMaginTables();
        }

        public Dictionary<string, string> OpenOrder()
        {
            string strOrderType;
            string strOrderBTCPrice;
            Dictionary<string, string> dicOpenOrder = new Dictionary<string, string>();

            DataTable table = _dsMarketData.Tables(DataMarket.TABLE_KORBIT_OPEN);

            foreach (DataRow dr in table.Rows)
            {
                strOrderType = dr[DataMarket.COLUMN_ORDER_TYPE].ToString();
                strOrderBTCPrice = dr[DataMarket.COLUMN_BTC_PRICE].ToString();

                dicOpenOrder.Add(strOrderType, strOrderBTCPrice);
            }

            return dicOpenOrder;
        }

        private void CalculateAccTables()
        {
            CalculateAccTable(DataMarket.TABLE_KORBIT_ASK, DataArbitrage.TABLE_ACC_KORBIT_ASK, eCalcAccTable.ASK);
            CalculateAccTable(DataMarket.TABLE_KORBIT_BID, DataArbitrage.TABLE_ACC_KORBIT_BID, eCalcAccTable.BID);
            CalculateAccTable(DataMarket.TABLE_BTCE_ASK, DataArbitrage.TABLE_ACC_BTCE_ASK, eCalcAccTable.ASK);
            CalculateAccTable(DataMarket.TABLE_BTCE_BID, DataArbitrage.TABLE_ACC_BTCE_BID, eCalcAccTable.BID);
        }

        private void CalculateAccTable(string strSrcTable, string strTgtTable, eCalcAccTable eCalc)
        {
            double dbPrice;
            double dbAmount;
            double dbAveragePrice;
            double dbAccAmount;
            double dbTotalAmount;
            double dbCurrency;
            DataRow[] dRows;

            dbCurrency = Double.Parse(_market.CurrentRate());

            DataTable srcTable = _dsMarketData.Tables(strSrcTable);
            DataTable tgtTable = _dsTradingData.Tables(strTgtTable).Clone();

            if (tgtTable == null)
                return;

            tgtTable.Clear();

            if (eCalc == eCalcAccTable.ASK)
            {
                dRows = srcTable.Select("", DataMarket.COLUMN_BTC_PRICE + " ASC");
            }
            else
            {
                dRows = srcTable.Select("", DataMarket.COLUMN_BTC_PRICE + " DESC");
            }


            dbAveragePrice = 0.0;
            dbTotalAmount = 0.0;
            dbAccAmount = 0.0;

            tgtTable.Rows.Clear();
            foreach (DataRow dr in dRows)
            {
                dbPrice = Double.Parse(dr[DataMarket.COLUMN_BTC_PRICE].ToString());
                dbAmount = Double.Parse(dr[DataMarket.COLUMN_BTC_VOLUME].ToString());
                dbAccAmount = dbAccAmount + dbAmount;
                dbTotalAmount = dbTotalAmount + dbPrice * dbAmount;
                dbAveragePrice = dbTotalAmount / dbAccAmount;

                //새로운 행을 가져온다.
                DataRow row = tgtTable.NewRow();
                row[DataArbitrage.COLUMN_ACC_BTCPRICE] = dbPrice;
                row[DataArbitrage.COLUMN_ACC_BTCPRICE_AVG] = dbAveragePrice;
                row[DataArbitrage.COLUMN_ACC_BTCAMOUNT] = dbAccAmount;
                tgtTable.Rows.Add(row);
            }

            _dsTradingData.SetDataTable(strTgtTable, tgtTable);
        }

        private void CalculateMaginTables()
        {
            CalculateMaginTable(DataArbitrage.TABLE_ACC_KORBIT_ASK, DataArbitrage.TABLE_ACC_BTCE_BID, DataArbitrage.TABLE_MARGINBUY, eCalcMarginTable.BUY);
            CalculateMaginTable(DataArbitrage.TABLE_ACC_BTCE_ASK, DataArbitrage.TABLE_ACC_KORBIT_BID, DataArbitrage.TABLE_MARGINSELL, eCalcMarginTable.SELL);
        }

        private void CalculateMaginTable(string strBuyTable, string strSellTable, string strTargetTable, eCalcMarginTable eCalc)
        {
            double dbUSDKRW;

            double dbEstIncome;
            double dbEstPerIncome;
            double dbBuyBTCPrice;
            double dbBuyBTCAmount;
            double dbBuyAvgBTCPrice;
            double dbSellBTCPrice;
            double dbSellBTCAmount;
            double dbSellAvgBTCPrice;

            double dbTradeBTC;
            double dbDiscountRate;

            //데이터 설정
            DataTable buyTable = _dsTradingData.Tables(strBuyTable);
            DataTable sellTable = _dsTradingData.Tables(strSellTable);
            DataTable tgtTable = _dsTradingData.Tables(strTargetTable).Clone();

            DataRow[] buyRows = buyTable.Select("", DataArbitrage.COLUMN_ACC_BTCPRICE + " ASC");
            DataRow[] sellRows = sellTable.Select("", DataArbitrage.COLUMN_ACC_BTCPRICE + " DESC");


            //지갑 가져오기
            dbDiscountRate = 0.002;
            dbUSDKRW = Double.Parse(_market.CurrentRate());

            tgtTable.Clear();

            if (buyTable.Rows.Count < 50 || sellTable.Rows.Count < 50)
                return;

            for (int iBuyIndex = 0; iBuyIndex < 50; iBuyIndex++)
            {
                for (int iSellIndex = 0; iSellIndex < 50; iSellIndex++)
                {
                    dbBuyBTCPrice = Double.Parse(buyRows[iBuyIndex][DataArbitrage.COLUMN_ACC_BTCPRICE].ToString());
                    dbBuyBTCAmount = Double.Parse(buyRows[iBuyIndex][DataArbitrage.COLUMN_ACC_BTCAMOUNT].ToString());
                    dbBuyAvgBTCPrice = Double.Parse(buyRows[iBuyIndex][DataArbitrage.COLUMN_ACC_BTCPRICE_AVG].ToString());
                    dbSellBTCPrice = Double.Parse(sellRows[iSellIndex][DataArbitrage.COLUMN_ACC_BTCPRICE].ToString());
                    dbSellBTCAmount = Double.Parse(sellRows[iSellIndex][DataArbitrage.COLUMN_ACC_BTCAMOUNT].ToString());
                    dbSellAvgBTCPrice = Double.Parse(sellRows[iSellIndex][DataArbitrage.COLUMN_ACC_BTCPRICE_AVG].ToString());

                    if (eCalc == eCalcMarginTable.BUY)
                    {
                        dbEstPerIncome = dbSellAvgBTCPrice * dbUSDKRW * (1.0 - dbDiscountRate) - dbBuyAvgBTCPrice;
                    }
                    else
                    {
                        dbEstPerIncome = dbSellAvgBTCPrice - dbBuyAvgBTCPrice * dbUSDKRW * (1.0 + dbDiscountRate);
                    }

                    if (dbSellBTCAmount < dbBuyBTCAmount)
                    {
                        dbTradeBTC = dbSellBTCAmount;
                    }
                    else
                    {
                        dbTradeBTC = dbBuyBTCAmount;
                    }

                    dbEstIncome = dbEstPerIncome * dbTradeBTC;
                    
                    DataRow dr = tgtTable.NewRow();
                    dr[DataArbitrage.COLUMN_EST_PER_INCOME] = dbEstPerIncome;
                    dr[DataArbitrage.COLUMN_EST_BTCAMOUNT] = dbTradeBTC;
                    dr[DataArbitrage.COLUMN_EST_BUY_BTCPRICE] = dbBuyBTCPrice;
                    dr[DataArbitrage.COLUMN_EST_SELL_BTCPRICE] = dbSellBTCPrice;
                    tgtTable.Rows.Add(dr);

                }

                _dsTradingData.SetDataTable(strTargetTable, tgtTable);
            }
        }


        public void CalculateMarginalTrade()
        {
        }

        public string KorbitBestAsk()
        {
            return BestASKBID(eOrderTable.KORBIT_ASK);
        }

        public string KorbitBestBid()
        {
            return BestASKBID(eOrderTable.KORBIT_BID);
        }

        public string BTCeBestAsk()
        {
            return BestASKBID(eOrderTable.BTCE_ASK);
        }

        public string BTCeBestBid()
        {
            return BestASKBID(eOrderTable.BTCE_BID);
        }

        public string MarginalBuyPrice()
        {
            return BestMarginalPrice(eCalcMarginTable.BUY);
        }

        public string MarginalSellPrice()
        {
            return BestMarginalPrice(eCalcMarginTable.SELL);
        }

        public DataTable KorbitAccAsk()
        {
            return _dsTradingData.Tables(DataArbitrage.TABLE_ACC_KORBIT_ASK);
        }

        public DataTable KorbitAccBid()
        {
            return _dsTradingData.Tables(DataArbitrage.TABLE_ACC_KORBIT_BID);
        }

        public DataTable BTCeAccAsk()
        {
            return _dsTradingData.Tables(DataArbitrage.TABLE_ACC_BTCE_ASK);
        }

        public DataTable BTCeAccBid()
        {
            return _dsTradingData.Tables(DataArbitrage.TABLE_ACC_BTCE_BID);
        }

        public DataTable MarginalBuy()
        {
            return _dsTradingData.Tables(DataArbitrage.TABLE_MARGINBUY);
        }

        public DataTable MarginalSell()
        {
            return _dsTradingData.Tables(DataArbitrage.TABLE_MARGINSELL);
        }

        private string BestASKBID(eOrderTable eOrder)
        {
            string strReturn = "0.0";
            string strTableName;

            switch(eOrder)
            {
                case eOrderTable.KORBIT_ASK:
                    strTableName = DataArbitrage.TABLE_ACC_KORBIT_ASK;
                    break;
                case eOrderTable.KORBIT_BID:
                    strTableName = DataArbitrage.TABLE_ACC_KORBIT_BID;
                    break;
                case eOrderTable.BTCE_ASK:
                    strTableName = DataArbitrage.TABLE_ACC_BTCE_ASK;
                    break;
                case eOrderTable.BTCE_BID:
                    strTableName = DataArbitrage.TABLE_ACC_BTCE_BID;
                    break;
                default:
                    strTableName = "";
                    break;
            }

            DataTable dt = _dsTradingData.Tables(strTableName);

            if (dt == null)
                return strReturn;

            DataRow[] rows;

            if (eOrder == eOrderTable.KORBIT_ASK || eOrder == eOrderTable.BTCE_ASK)
            {
                rows = dt.Select(DataArbitrage.COLUMN_ACC_BTCAMOUNT + " > 1.0", DataArbitrage.COLUMN_ACC_BTCPRICE_AVG + " ASC");
            }
            else
            {
                rows = dt.Select(DataArbitrage.COLUMN_ACC_BTCAMOUNT + " > 1.0", DataArbitrage.COLUMN_ACC_BTCPRICE_AVG + " DESC");
            }

            if(rows.Length == 0)
                return strReturn;

            strReturn = (rows[0])[DataArbitrage.COLUMN_ACC_BTCPRICE].ToString();
            
            return strReturn;
        }

        private string BestMarginalPrice(eCalcMarginTable eCalc)
        {
            string strReturn = "0.0";
            DataRow dr = null;

            dr = MarginalProfitRow(eCalc);

            if (dr == null)
                return strReturn;

            strReturn = dr[DataArbitrage.COLUMN_EST_PER_INCOME].ToString();

            return strReturn;
        }

        private DataRow MarginalProfitRow(eCalcMarginTable eCalc)
        {
            DataRow dr = null;

            string strTableName;

            if (eCalc == eCalcMarginTable.BUY)
                strTableName = DataArbitrage.TABLE_MARGINBUY;
            else
                strTableName = DataArbitrage.TABLE_MARGINSELL;

            DataTable dt = _dsTradingData.Tables(strTableName);

            if (dt == null)
                return dr;

            DataRow[] rows = dt.Select(DataArbitrage.COLUMN_EST_BTCAMOUNT + " > 1.0", DataArbitrage.COLUMN_EST_PER_INCOME + " DESC");

            if (rows.Length == 0)
                return dr;

            dr = rows[0];

            return dr;
        }

        public MarginalProfit MarginalProfitData(eCalcMarginTable eCalc)
        {
            MarginalProfit objReturn = new MarginalProfit();

            DataRow row = MarginalProfitRow(eCalc);

            if (row != null)
            {
                objReturn._eCalc = eCalc;
                objReturn._dbEstProfit = Double.Parse(row[DataArbitrage.COLUMN_EST_PER_INCOME].ToString());
                objReturn._dbBTCAmount = Double.Parse(row[DataArbitrage.COLUMN_EST_BTCAMOUNT].ToString());

                if (eCalc == eCalcMarginTable.BUY)
                {
                    objReturn._dbKRWPrice = Double.Parse(row[DataArbitrage.COLUMN_EST_BUY_BTCPRICE].ToString());
                    objReturn._dbUSDPrice = Double.Parse(row[DataArbitrage.COLUMN_EST_SELL_BTCPRICE].ToString());
                }
                else
                {
                    objReturn._dbUSDPrice = Double.Parse(row[DataArbitrage.COLUMN_EST_BUY_BTCPRICE].ToString());
                    objReturn._dbKRWPrice = Double.Parse(row[DataArbitrage.COLUMN_EST_SELL_BTCPRICE].ToString());
                }
            }

            return objReturn;
        }
    }

    enum eCalcAccTable
    {
        ASK,
        BID
    }

    enum eOrderTable
    {
        KORBIT_ASK,
        KORBIT_BID,
        BTCE_ASK,
        BTCE_BID
    }
}
